﻿using System.Collections.Generic;

namespace FlatBuffers
{
    public class FlatBufferBuilderPool : Singleton<FlatBufferBuilderPool>
    {
        private const int kCacheSize = 128;
        private const int kBufferInitialSize = 1;
        private const int kCachedHandleSize = 128;

        private FlatBufferBuilder[] _cached_builders = new FlatBufferBuilder[kCacheSize];
        private byte[] _cached_builders_state = new byte[kCacheSize];
        private int _fetch_index = 0;
        private List<AutoHandle> _cached_auto_handles = null;


        public class AutoHandle
        {
            private FlatBufferBuilder _builder = null;
            private int _index = 0;

            public FlatBufferBuilder Value
            {
                get
                {
                    return _builder;
                }
            }           

            public void Set(FlatBufferBuilder builder, int index)
            {
                _builder = builder;
                _index = index;
            }

            public AutoHandle() { }
            public AutoHandle(FlatBufferBuilder builder, int index)
            {
                Set(builder, index);
            }

            public void Release ()
            {
                FlatBufferBuilderPool.instance.Release(_index, _builder);
                FlatBufferBuilderPool.instance.ReleaseAutoHandle(this);
            }
        }

        public override void Init()
        {
            for (int ii = 0; ii < kCacheSize; ++ii)
            {
                _cached_builders[ii] = new FlatBufferBuilder(kBufferInitialSize);
                _cached_builders_state[ii] = (byte)0;
            }

            _cached_auto_handles = new List<AutoHandle>(kCachedHandleSize);
            for (int ii = 0; ii < kCachedHandleSize; ++ii)
            {
                _cached_auto_handles.Add(new AutoHandle());
            }
        }

        public AutoHandle Fetch()
        {
            FlatBufferBuilder builder = null;
            int count = 0;
            int id = -1;
            while (count < kCacheSize && _cached_builders_state[_fetch_index] != 0)
            {
                _fetch_index = (_fetch_index + 1) % kCacheSize;
                ++count;
            }

            if (_cached_builders_state[_fetch_index] == 0)
            {
                id = _fetch_index;
                builder = _cached_builders[_fetch_index];
                _cached_builders_state[_fetch_index] = 1;
                _fetch_index = (_fetch_index + 1) % kCacheSize;
            }

            if (builder != null)
            {
                AutoHandle handle = FetchHandle(builder, id);//new AutoHandle(builder, id);
                return handle;
            }

            return null;
        }

        private AutoHandle FetchHandle (FlatBufferBuilder builder, int index)
        {            
            if (_cached_auto_handles.Count > 0)
            {
                AutoHandle handle = _cached_auto_handles[0];
                _cached_auto_handles.RemoveAt(0);
                handle.Set(builder, index);
                return handle;
            }

            return new AutoHandle(builder, index);
        }

        private void Release(int index, FlatBufferBuilder builder)
        {
            builder.Clear();
            _cached_builders_state[index] = 0;
        }

        private void ReleaseAutoHandle(AutoHandle handle)
        {
            if (handle != null)
            {
                _cached_auto_handles.Add(handle);
            }
        }
    }
}